/**
 * View package.
 */
package fr.umlv.m2.ig.view;

import java.awt.Dimension;
import java.awt.Point;

/**
 * This class defines the handler
 * which computes operations after a zoom or a drag
 * on the subway map.
 * 
 * @author Gambouele Bertrand.
 * @author Viravout Alexandre.
 */
public class ZoomAndDragHandler {

	/**
	 * The current scale value.
	 */
	private int zoom;
	
	/**
	 * Map width
	 */
	private final int mapWidth;
	
	/**
	 * Map height
	 */
	private final int mapHeight;
	
	/**
	 * Size of the currently displayed image.
	 */
	private final Dimension newSize;
	
	/**
	 * Creates a new {@code ZoomAndDragHandler} object
	 * with specified initial values.
	 * 
	 * @param initialScale initial scale value.
	 * @param mapWith map width.
	 * @param mapHeight map height.
	 */
	public ZoomAndDragHandler(final int initialScale, final int mapWith, final int mapHeight) {
		this.zoom = initialScale;
		this.mapWidth = mapWith;
		this.mapHeight = mapHeight;
		this.newSize = new Dimension(mapWith, mapHeight);
	}
	
	/**
	 * Performs actions after a zoom in with mouse.
	 * 
	 * @param topLeftCorner top left corner location.
	 * @param mouseCoord mouse coordinates.
	 * @return the new scale factor.
	 */
	public int performZoomInWithMouse(final Point topLeftCorner, final Point mouseCoord) {
		if (zoom * zoom < Math.min(mapWidth, mapHeight)) {
			zoom++;
			updateCorner(topLeftCorner, mouseCoord);
		}
		
		return zoom;
	}
	
	/**
	 * Performs actions after a zoom out with mouse.
	 * 
	 * @param topLeftCorner Top left corner location.
	 * @param mouseCoord mouse coordinates.
	 * @return the new scale factor.
	 */
	public int performZoomOutWithMouse(final Point topLeftCorner, final Point mouseCoord) {
		if (zoom > 1) {
			zoom--;
			
			if (zoom == 1) {
				topLeftCorner.move(0, 0);
				newSize.setSize(mapWidth, mapHeight);
			} else {
				updateCorner(topLeftCorner, mouseCoord);
				adjustCorner(topLeftCorner);
			}
		}
		
		return zoom;
	}
	
	/**
	 * Performs actions after a drag with mouse.
	 * 
	 * @param lastTopLeftCorner Last top left corner location.
	 * @param newTopLeftCorner Will contains the top left corner location.
	 * @param lastMouseCoord Mouse coordinates before drag.
	 * @param newMouseCoord Mouse coordinates after drag.
	 * @return true if the top left corner has changed or false otherwise.
	 */
	public boolean performDragWithMouse(final Point lastTopLeftCorner, final Point newTopLeftCorner, final Point lastMouseCoord, final Point newMouseCoord) {
		double dx = (lastMouseCoord.getX() - newMouseCoord.getX()) / zoom;
		double dy = (lastMouseCoord.getY() - newMouseCoord.getY()) / zoom;
		
		newTopLeftCorner.setLocation(lastTopLeftCorner.x + dx, lastTopLeftCorner.y + dy);
		adjustCorner(newTopLeftCorner);
		
		return !(lastTopLeftCorner.equals(newTopLeftCorner));
	}
	
	/**
	 * Moves the top left corner after a drag or a zoom.
	 * 
	 * @param topLeftCorner the current top left corner location.
	 * @param mouseCoord Mouse coordinates.
	 */
	private void updateCorner(final Point topLeftCorner, final Point mouseCoord) {
		int centerX = topLeftCorner.x + newSize.width/2;
		int centerY = topLeftCorner.y + newSize.height/2;
		
		newSize.setSize(mapWidth / zoom, mapHeight / zoom);

		int dx = newSize.width / 2;
		int dy = newSize.height / 2;
		
		topLeftCorner.move(centerX - dx, centerY - dy);
	}

	/**
	 * After a zoom or a drag and drop operation, call this method to crop the
	 * new corners if they ever are out of the real image bounds.
	 * 
	 * @param topLeftCorner the current top left corner location.
	 */
	private void adjustCorner(final Point topLeftCorner) {
		if (topLeftCorner.x < 0) {
			topLeftCorner.x = 0;
		}

		if (topLeftCorner.y < 0) {
			topLeftCorner.y = 0;
		}
		
		if (topLeftCorner.x + newSize.width > mapWidth) {
			topLeftCorner.x = mapWidth - newSize.width;
		}
		
		if (topLeftCorner.y + newSize.height > mapHeight) {
			topLeftCorner.y = mapHeight - newSize.height;
		}
	}
}
